﻿// SMIL is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warrenty – Use at your own risk
// View the project and the latest code at http://smil.codeplex.com/


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using DeepEarth.BingMapsToolkit.Client.MapGeometry;
using DeepEarth.BingMapsToolkit.Client.Common;
using DeepEarth.BingMapsToolkit.Client.Common.Commanding;
using DeepEarth.BingMapsToolkit.Client.Common.Entities;
using DeepEarth.BingMapsToolkit.Common.Entities;
using DeepEarth.BingMapsToolkit.Client.Controls;
using System.ServiceModel;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using SMIL.Client.WPXAP.PushPin;
using SMIL.Client.WPXAP.SPCommunication;
using Microsoft.Maps.MapControl;
using Map = Microsoft.Maps.MapControl.Map;
using Point = GisSharpBlog.NetTopologySuite.Geometries.Point;
using IsolatedStorage = DeepEarth.BingMapsToolkit.Client.Common.IsolatedStorage;

namespace SMIL.Client.WPXAP.LayerPanel
{
    public class SPGeoRSSItem
    {
        public string title;
        public Guid guid;
        public string description;        
        public string position;
        public string icon_url;
        public string link_url;
        public string overlay_url;

        public string georss_point;
        public string geo_lat, geo_long;

        private string[] image_extensions = new string[] { ".jpg", ".png" };

        public class Detail
        {
            public string fieldName;
            public string fieldType;
            public string value;
        }

        public Dictionary<string, Detail> details;        

        public bool IsImage
        {
            get
            {
                if (link_url == null)
                    return false;

                foreach (string extension in image_extensions)
                    if (link_url.ToLower().EndsWith(extension))
                        return true;

                return false;
            }
        }

        public bool ContainsCoordinate 
        {
            get
            {
                return georss_point != null || (geo_lat != null && geo_long != null);
            }
        }

        public double Latitude
        {
            get
            {
                if (geo_lat == null && georss_point != null)
                    geo_lat = (georss_point.Split(new char[] { ' ' }))[0];

                geo_lat = geo_lat.Replace(",", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
                geo_lat = geo_lat.Replace(".", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);

                return double.Parse(geo_lat);
            }
        }

        public double Longitude
        {
            get
            {
                if (geo_long == null && georss_point != null)
                    geo_long = (georss_point.Split(new char[] { ' ' }))[1];

                geo_long = geo_long.Replace(",", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
                geo_long = geo_long.Replace(".", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);

                return double.Parse(geo_long);
            }
        }
    }

    public enum SMILLayerType
    {        
        SMIL        
    }

    public class SMILLayerDefinition : LayerDefinition
    {
        //public Dictionary<string, string> parameters = new Dictionary<string, string>();
        
        //public string Path;
        public new SMILLayerType LayerType;

        public string [] ViewNames;

        public string ListURL;
        public double MaxZoomLevel;
        public double MinZoomLevel;  
    }

    public class LayerPanel : DeepEarth.BingMapsToolkit.Client.Controls.TreeViewLayerPanel
    {
        const string ISOKey = "SMIL.Client.WPXAP.LayerPanel";

        SPCommunication.ASMX.SPGeoRssSISoapClient sp_proxy;
        
        EventHandler<MapEventArgs> viewChangeOnFrame;
        TextBlock PART_LayersTitle;        

        public LayerPanel()
        {            
            this.DefaultStyleKey = typeof(LayerPanel);
            Loaded += LayerPanel_Loaded;
        }

        private void LayerPanel_Loaded(object sender, RoutedEventArgs e)
        {
            viewChangeOnFrame = new EventHandler<MapEventArgs>(MapInstance_ViewChangeOnFrame);            
     
            sp_proxy = SPCommunication.Proxy.GetProxy;
            sp_proxy.GetLayerListCompleted += new EventHandler<SMIL.Client.WPXAP.SPCommunication.ASMX.GetLayerListCompletedEventArgs>(sp_proxy_GetLayerListCompleted);
            sp_proxy.GetGeoRssForLayerCompleted += new EventHandler<SMIL.Client.WPXAP.SPCommunication.ASMX.GetGeoRssForLayerCompletedEventArgs>(sp_proxy_GetGeoRssForLayerCompleted);
        }                
               
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            PART_LayersTitle = GetTemplateChild("PART_LayersTitle") as TextBlock;
        }
       
        public string SPSiteURI { get; set; }

        public string GeoRssURI { get; set; }

        public void Connect()
        {                                                
            savePersistentState();                            
            sp_proxy.GetLayerListAsync(new SMIL.Client.WPXAP.SPCommunication.ASMX.LayersRequest() 
            { 
                SiteUrl = SPSiteURI 
            });

            SPCommunication.ASMX.ItemDetailResponse idr = new SMIL.Client.WPXAP.SPCommunication.ASMX.ItemDetailResponse();            

            //layerTreeviewFadeOut.Dispatcher.BeginInvoke(() => layerTreeviewFadeOut.Begin());


            if (Layers != null)
                foreach (LayerDefinition ld in Layers)
                    UnloadLayer(ld);                        
        }


        void sp_proxy_GetLayerListCompleted(object sender, SMIL.Client.WPXAP.SPCommunication.ASMX.GetLayerListCompletedEventArgs e)
        {
            List<SMILLayerDefinition> kl = new List<SMILLayerDefinition>();

            foreach (SPCommunication.ASMX.LayerResponseObj lro in e.Result.Layers)
            {
                foreach (string viewname in lro.ViewNames)
                {
                    if (viewname.Length < 1)
                        continue;

                    SMILLayerDefinition kld = new SMILLayerDefinition()
                    {
                        ViewNames = lro.ViewNames.ToArray(),
                        LayerAlias = viewname,
                        LayerID = lro.Name + "/" + viewname, // Använd lagernamn och vynamn för att få ett unikt ID
                        LayerType = (SMILLayerType)Enum.Parse(SMILLayerType.SMIL.GetType(), lro.Type, true),
                        Selected = false,
                        Tags = lro.Name,
                        MinDisplayLevel = 1,
                        MaxDisplayLevel = 21,
                        MinZoomLevel = 1,
                        MaxZoomLevel = 21
                    };

                    // Ordna lagret i Z-led baserat på typ
                    switch (kld.LayerType)
                    {                        
                        case SMILLayerType.SMIL:
                            kld.ZIndex = 4;
                            break;                    

                        default:
                            kld.ZIndex = 0;
                            break;
                    }

                    kl.Add(kld);
                }
            }

            compareAndloadPersistentState(kl);

            MapInstance.Children.Clear();        

            Layers = kl.Cast<LayerDefinition>().ToList();


            MapInstance.ViewChangeOnFrame -= viewChangeOnFrame;
            MapInstance.ViewChangeOnFrame += viewChangeOnFrame;
        }       

        private void compareAndloadPersistentState(List<SMILLayerDefinition> kld)
        {
            List<SMILLayerDefinition> layerListState = IsolatedStorage.LoadData<List<SMILLayerDefinition>>(ISOKey);

            if (layerListState != default(List<SMILLayerDefinition>) && layerListState.Count == kld.Count)
            {
                for (int i = 0; i < layerListState.Count; i++)
                {
                    // LayerID (Path i SP-listan), är lika oavsett språk 
                    kld[i].Selected = (layerListState[i].LayerID == kld[i].LayerID) ? layerListState[i].Selected : false;
                }
            }
        }

        public void savePersistentState()
        {
            if (Layers != null)
            {
                List<SMILLayerDefinition> l = Layers.Cast<SMILLayerDefinition>().ToList();
                IsolatedStorage.SaveData<List<SMILLayerDefinition>>(l, ISOKey);
            }
        }

        protected override void LoadLayer(LayerDefinition layerDefinition)
        {           
            SMILLayerDefinition kld = layerDefinition as SMILLayerDefinition;

            UpdateOverlaysVisibility();

            try
            {
                switch (kld.LayerType)
                {
                    case SMILLayerType.SMIL:
                        EnhancedMapLayer eml = Vector.GetLayerByID(layerDefinition.LayerID, MapInstance);

                        if (eml == null)
                        {                                                                                
                            sp_proxy.GetGeoRssForLayerAsync(new SMIL.Client.WPXAP.SPCommunication.ASMX.LayerRequest() { SiteUrl = SPSiteURI, SPListName = kld.Tags, SPViewName = kld.LayerAlias }, kld);
                        }
                        else
                        {                            
                            foreach (XAMLPushPin xpp in eml.Children)
                                xpp.LoadAnimation();
                        }

                        savePersistentState();

                        break;                  
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error loading layer <" + layerDefinition.LayerAlias + " / " + layerDefinition.LayerID + "> : " + ex.Message);
            }            
        }

        protected override void UnloadLayer(LayerDefinition layerDefinition)
        {
            savePersistentState();

            SMILLayerDefinition kld = layerDefinition as SMILLayerDefinition;            

            switch (kld.LayerType)
            {
                case SMILLayerType.SMIL:
                    
                    EnhancedMapLayer eml = Vector.GetLayerByID(layerDefinition.LayerID, MapInstance);

                    if (eml != null)
                    {
                        foreach (XAMLPushPin xpp in eml.Children)
                            xpp.UnloadAnimation();                        
                    }
                    
                    break;           
            }
        }
        
        /*******************************************************************
         * 
         * SMIL rendering
         * 
         *******************************************************************/
        void sp_proxy_GetGeoRssForLayerCompleted(object sender, SMIL.Client.WPXAP.SPCommunication.ASMX.GetGeoRssForLayerCompletedEventArgs e)
        {                 
            PopulateLayer((SMILLayerDefinition)e.UserState, e.Result.Response);
        }   
        
        void PopulateLayer(SMILLayerDefinition l, XElement xml_layer)
        {
            XNamespace xmlns = "http://www.w3.org/2005/Atom";
            XNamespace georssns = "http://www.georss.org/georss";
            XNamespace geons = "http://www.w3.org/2003/01/geo/wgs84_pos#";

            if (xml_layer == null)
                return;

            var items = from item in xml_layer.Descendants("item")
                        select new SPGeoRSSItem()
                        {
                            title = (string)item.Element("title"),                            
                            description = (string)item.Element("description"),                            
                            georss_point = (item.Element(georssns + "point") != null) ? item.Element(georssns + "point").Value : null,
                            geo_lat = (item.Element(geons + "lat") != null) ? item.Element(geons + "lat").Value : null,
                            geo_long = (item.Element(geons + "long") != null) ? item.Element(geons + "long").Value : null,
                            icon_url = (string)item.Element("icon"),
                            link_url = (string)item.Element("link"),
                            overlay_url = (string)item.Element("checkoutoverlay_url"),
                            
                            details = (from detail in item.Descendants("detail")
                                      select detail).ToDictionary(param => param.Attribute("fieldName").Value.ToString(), 
                                                                  param => new SPGeoRSSItem.Detail()
                                                                  {
                                                                      fieldName = param.Attribute("fieldName").Value.ToString(),
                                                                      fieldType = param.Attribute("type").Value.ToString(),
                                                                      value = param.Value.ToString()                                                                      
                                                                  })                            
                        };           

            CreatePushpinOverlay(items, l);
        }

        void CreatePushpinOverlay(IEnumerable<SPGeoRSSItem> layerData, SMILLayerDefinition kld)
        {            
            var pushpins = new List<XAMLPushPin>();

            switch (kld.LayerType)
            {
                case SMILLayerType.SMIL:
                    foreach (SPGeoRSSItem si in layerData)
                    {
                        if (!si.ContainsCoordinate)
                            continue;

                        //string iconUrl = kld.SMILIconURL;

                        //if (iconUrl == null || iconUrl == string.Empty)
                        //{
                            //iconUrl = (si.IsImage) ? si.link_url : si.icon_url;
                        //}                                                                     

                        var pin = new SMILPin(MapInstance)
                        {                 
                            Guid = new Guid(si.details["guid"].value),
                            ViewName = kld.LayerAlias,
                            ViewNames = kld.ViewNames,
                            ListName = kld.Tags,
                            SPSiteURI = SPSiteURI,
                            Title = si.title,                            
                            Subtitle = kld.Tags + " -> " + kld.LayerAlias,
                            PinLocation = new Location(si.Latitude, si.Longitude),
                            ObjectUrl = si.link_url,
                            IconUrl = (si.IsImage) ? si.link_url : si.icon_url
                        };

                        pushpins.Add(pin);
                    }
                    break;             
            }            
            
            // Skapa ett layer utan objekt först...
            Vector.CreateOverlay(new ObservableCollection<VectorLayerData>(), kld, MapInstance, null, false, true, null);

            // ..och befolka det manuellt 
            EnhancedMapLayer eml = Vector.GetLayerByID(kld.LayerID, MapInstance);            

            foreach (XAMLPushPin dpp in pushpins)
            {
                eml.AddChild(dpp, dpp.PinLocation, PositionOrigin.Center);
            }

            // Sätt eventhandler på vad som ska hända när det sista objektet i ett lager har kört 
            // klar sin animation (vid avaktivering av lagret).
            if (pushpins.LastOrDefault() != default(XAMLPushPin))
            {               
                pushpins.LastOrDefault().UnloadAnimationCompleted += new EventHandler((o, e) => eml.Visibility = Visibility.Collapsed );                
            }                    
        }          
    
        void SetOverlayVisibility(LayerDefinition layerDefinition, Visibility visibility)
        {
            var vl = Vector.GetLayerByID(layerDefinition.LayerID, MapInstance);

            if (vl != null)
            {
                vl.Visibility = visibility;
                return;
            }
        }

        double _lastzoom;
        void MapInstance_ViewChangeOnFrame(object sender, MapEventArgs e)
        {
            if (MapInstance.ZoomLevel != _lastzoom)
            {
                UpdateOverlaysVisibility();
                _lastzoom = MapInstance.ZoomLevel;
            }
        }
        
        void UpdateOverlaysVisibility()
        {                                   
            foreach (LayerDefinition ld in Layers)
            {                    
                if (ld.Selected)
                {
                    SMILLayerDefinition kld = ld as SMILLayerDefinition;
                    Visibility visible = (MapInstance.ZoomLevel >= kld.MinZoomLevel && MapInstance.ZoomLevel <= kld.MaxZoomLevel) ? Visibility.Visible : Visibility.Collapsed;
                    SetOverlayVisibility(ld, visible);
                }
            }
        }
    } 
}